#pragma once

#include <cstddef>
#include <algorithm>
#include <cmath>

namespace mdtx::online::impl
{
    template <typename T>
    class lindecay
    {
    public:
        T a, decay{};

        explicit lindecay(ptrdiff_t period)
            : a(T(1) / period)
        {
        }

        void insert_impl(T x)
        {
            T x_signal = x != 0 ? 1 : 0;
            decay = std::max<T>(std::max(x_signal, decay - a), 0);
        }

        void reset_impl()
        {
            decay = 0;
        }
    };

    template <typename T>
    class expdecay
    {
    public:
        T a, decay{};

        explicit expdecay(T a)
            : a(a)
        {
        }

        void insert_impl(T x)
        {
            T x_signal = x != 0 ? 1 : 0;
            decay = std::max<T>(x_signal, decay * a);
        }

        void reset_impl()
        {
            decay = 0;
        }
    };

    template <typename T>
    class logret
    {
    public:
        T last, ret;
        bool init;

        void insert_impl(T x)
        {
            x = std::log(x);
            if (init)
                [[likely]]
            {
                ret = x - last;
            }
            else
                [[unlikely]]
            {
                init = true;
            }
            last = x;
        }

        void reset_impl()
        {
            init = false;
            ret = 0;
        }
    };

    template <typename T>
    class linret
    {
    public:
        T last, ret;
        bool init;

        void insert_impl(T x)
        {
            if (init)
                [[likely]]
            {
                ret = x / last + 1;
            }
            else
                [[unlikely]]
            {
                init = true;
            }
            last = x;
        }

        void reset_impl()
        {
            init = false;
            ret = 0;
        }
    };

    template <typename T>
    class absret
    {
    public:
        T last, ret;
        bool init;

        void insert_impl(T x)
        {
            if (init)
                [[likely]]
            {
                ret = x - last;
            }
            else
                [[unlikely]]
            {
                init = true;
            }
            last = x;
        }

        void reset_impl()
        {
            init = false;
            ret = 0;
        }
    };
}
